<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="keywords" content="Hexo Theme Keep">
    <meta name="description" content="Hexo Theme Keep">
    <meta name="author" content="jianyu">
    
    <title>
        
            Android Binder机制原理 |
        
        gyu&#39;s 时光
    </title>
    
<link rel="stylesheet" href="/css/style.css">

    <link rel="shortcut icon" href="/images/logo.svg">
    
<link rel="stylesheet" href="/css/font-awesome.min.css">

    <script id="hexo-configurations">
    let KEEP = window.KEEP || {};
    KEEP.hexo_config = {"hostname":"gyu.gitee.io","root":"/","language":"zh-CN","path":"search.xml"};
    KEEP.theme_config = {"toc":{"enable":true,"number":true,"expand_all":true,"init_open":true},"style":{"primary_color":"#0066CC","avatar":"https://cdn.jsdelivr.net/gh/XPoet/image-hosting@master/common-use/avatar.jpg","favicon":"/images/logo.svg","article_img_align":"left","left_side_width":"260px","content_max_width":"920px","hover":{"shadow":true,"scale":false},"first_screen":{"enable":true,"background_img":"/images/bg.svg","description":"只要开始，一切都来得及."},"scroll":{"progress_bar":{"enable":true},"percent":{"enable":true}}},"local_search":{"enable":true,"preload":true},"code_copy":{"enable":true,"style":"default"},"pjax":{"enable":false},"lazyload":{"enable":false},"version":"3.4.2"};
    KEEP.language_ago = {"second":"%s 秒前","minute":"%s 分钟前","hour":"%s 小时前","day":"%s 天前","week":"%s 周前","month":"%s 月前","year":"%s 年前"};
  </script>
<meta name="generator" content="Hexo 5.4.0"></head>


<body>
<div class="progress-bar-container">
    
        <span class="scroll-progress-bar"></span>
    

    
</div>


<main class="page-container">

    

    <div class="page-main-content">

        <div class="page-main-content-top">
            <header class="header-wrapper">

    <div class="header-content">
        <div class="left">
            <a class="logo-title" href="/">
                gyu&#39;s 时光
            </a>
        </div>

        <div class="right">
            <div class="pc">
                <ul class="menu-list">
                    
                        <li class="menu-item">
                            <a class=""
                               href="/"
                            >
                                首页
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/archives"
                            >
                                归档
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/categories"
                            >
                                分类
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/tags"
                            >
                                标签
                            </a>
                        </li>
                    
                    
                        <li class="menu-item search search-popup-trigger">
                            <i class="fas fa-search"></i>
                        </li>
                    
                </ul>
            </div>
            <div class="mobile">
                
                    <div class="icon-item search search-popup-trigger"><i class="fas fa-search"></i></div>
                
                <div class="icon-item menu-bar">
                    <div class="menu-bar-middle"></div>
                </div>
            </div>
        </div>
    </div>

    <div class="header-drawer">
        <ul class="drawer-menu-list">
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/">首页</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/archives">归档</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/categories">分类</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/tags">标签</a>
                </li>
            
        </ul>
    </div>

    <div class="window-mask"></div>

</header>


        </div>

        <div class="page-main-content-middle">

            <div class="main-content">

                
                    <div class="fade-in-down-animation">
    <div class="article-content-container">

        <div class="article-title">
            <span class="title-hover-animation">Android Binder机制原理</span>
        </div>

        
            <div class="article-header">
                <div class="avatar">
                    <img src="https://cdn.jsdelivr.net/gh/XPoet/image-hosting@master/common-use/avatar.jpg">
                </div>
                <div class="info">
                    <div class="author">
                        <span class="name">jianyu</span>
                        
                            <span class="author-label">Engineer</span>
                        
                    </div>
                    <div class="meta-info">
                        <div class="article-meta-info">
    <span class="article-date article-meta-item">
        <i class="fas fa-edit"></i>&nbsp;2021-02-05 20:30:53
    </span>
    
        <span class="article-categories article-meta-item">
            <i class="fas fa-folder"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/categories/Android/">Android</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    
    
        <span class="article-tags article-meta-item">
            <i class="fas fa-tags"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/tags/IPC/">IPC</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    

    
    
    
    
</div>

                    </div>
                </div>
            </div>
        

        <div class="article-content markdown-body">
            <h3 id="Binder初了解"><a href="#Binder初了解" class="headerlink" title="Binder初了解:"></a>Binder初了解:</h3><p> Binder是Android基于Linux的一种独特的IPC机制。所谓IPC，就是跨进程通信。线程是CPU调度的基本单位，而进程则是向系统申请资源的基本单位。同一个进程中的各个线程是可以相互访问内存的，因为这些线程中的变量都是在堆栈中的。例如在Java中，多个线程可以set/get同一个变量，当然，这会引起线程同步问题。进程之间是不能直接相互访问变量的，也就是不能直接通信，需要使用一些IPC机制。常见的IPC有以下几种类型</p>
<p>共享内存：利用内存缓存区，将需要共享的数据写入内存缓存中。这里每次通信有两次数据读写过程，如A需要访问B进程的内存，则需要先将B的数据写到内存缓存区，然后再将数据写到进程A的内存区。管道：可以理解为系统让两个文件读写同一个特殊的文件，将共享的数据在文件中储存套接字：类似于网络通信</p>
<p>Android由于相当于嵌入式设备，对性能和内存要求较高，因此Android系统内置了一套Binder机制。Binder的底层技术原理是Linux的内存映射技术，也即用户进程可以和内核进程共享同一块内存，这样就减少了内存读写的操作数。Binder是一种C/S结构，访问方是Client，被访问的进程是Server。C和S通过Binder Driver作为桥梁，通过ServiceManager拿到系统的Binder服务。</p>
<p>这里还涉及到面向对象编程中的对象调用问题，例如A进程需要调用B进程中的某个对象做个操作。由于只是共享了内存而无法直接共享对象（不是同一个进程，无法保证运行的是同一份代码）。与Web开发中的RPC类似，Binder使用了代理模式，A进程需要调用B进程中的某个类，就是A进程中生成一个B进程中对象的代理对象，当发现A和B是同一个进程时，就直接调用，如果不是，则走Binder的IPC。</p>
<p>Android开发中的Binder模式具体有一个调用接口、一个接口的Stub抽象类，一个Proxy静态类。Android提供了AIDL，将自动生成这三个类，并放到同一个外部类中，这样可以避免Proxy和Stub的类名重复。</p>
<p>以下列实例为例：BookManager接口支持addBook和getBooks两个方法</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">&#x2F;**</span><br><span class="line"> * Server能够提供的服务</span><br><span class="line"> *&#x2F;</span><br><span class="line">interface BookManager: IInterface &#123;</span><br><span class="line">    @Throws(RemoteException::class)</span><br><span class="line">    fun addBook(book: Book?)</span><br><span class="line"></span><br><span class="line">    @Throws(RemoteException::class)</span><br><span class="line">    fun getBooks(): List&lt;Book&gt;?</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>BookManager接口继承于IInterface接口，表示这个接口的实现是在另外一个进程中，并可提供服务。</p>
<p>BookManagerStub是BookManager接口驻留在Server进程的一个对象，也就是这个BookManagerStub是在Server进程中new出来的，在BookService中的onBind返回这个BookManagerStub的对象，BookClientActivity连接Service的回调中，把Service返回的BookManagerStub对象通过BookManagerStub的asInterface转换成BookManager对象，如果获取的BookManagerStub的对象是和Client同进程的，就直接用，否则就构造一个BookManagerProxy，用来与Server跨进程通信。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">abstract class BookManagerStub: Binder(), BookManager &#123;</span><br><span class="line"></span><br><span class="line">    companion object &#123;</span><br><span class="line">        const val DESCRIPTIOR &#x3D; &quot;BookManager&quot;</span><br><span class="line">        const val TRANSACTION_ADD_BOOK &#x3D; 0</span><br><span class="line">        const val TRANSACTION_GET_BOOK &#x3D; 1</span><br><span class="line">        fun asInterface(binder: IBinder?): BookManager? &#123;</span><br><span class="line">            if (binder &#x3D;&#x3D; null) &#123;</span><br><span class="line">                return null</span><br><span class="line">            &#125;</span><br><span class="line">            val inter &#x3D; binder.queryLocalInterface(DESCRIPTIOR)</span><br><span class="line">            return (inter as? BookManager)?: BookManagerProxy(binder)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean &#123;</span><br><span class="line">        when(code) &#123;</span><br><span class="line">            IBinder.INTERFACE_TRANSACTION -&gt; &#123;</span><br><span class="line">                reply?.writeString(DESCRIPTIOR)</span><br><span class="line">                return true</span><br><span class="line">            &#125;</span><br><span class="line">            TRANSACTION_ADD_BOOK -&gt; &#123;</span><br><span class="line">                data?.enforceInterface(DESCRIPTIOR)</span><br><span class="line">                var book:Book? &#x3D; null</span><br><span class="line">                if (data?.readInt() !&#x3D; 0) &#123;</span><br><span class="line">                    book &#x3D; Book.CREATOR.createFromParcel(data)</span><br><span class="line">                &#125;</span><br><span class="line">                addBook(book)</span><br><span class="line">                reply?.writeNoException()</span><br><span class="line">                return true</span><br><span class="line">            &#125;</span><br><span class="line">            TRANSACTION_GET_BOOK -&gt; &#123;</span><br><span class="line">                data.enforceInterface(DESCRIPTIOR)</span><br><span class="line">                val result &#x3D; getBooks()</span><br><span class="line">                reply?.writeNoException()</span><br><span class="line">                reply?.writeTypedList(result)</span><br><span class="line">                return true</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        return super.onTransact(code, data, reply, flags)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    override fun asBinder(): IBinder &#123;</span><br><span class="line">        return this</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class BookService: Service() &#123;</span><br><span class="line"></span><br><span class="line">    companion object &#123;</span><br><span class="line">        const val TAG &#x3D; &quot;BookService&quot;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private val books &#x3D; arrayListOf&lt;Book&gt;()</span><br><span class="line"></span><br><span class="line">    private val bookManager &#x3D; object :BookManagerStub() &#123;</span><br><span class="line">        override fun addBook(book: Book?) &#123;</span><br><span class="line">            if (book &#x3D;&#x3D; null) &#123;</span><br><span class="line">                return</span><br><span class="line">            &#125;</span><br><span class="line">            books.add(book)</span><br><span class="line">            Log.d(TAG, &quot;addBook&quot;)</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        override fun getBooks(): List&lt;Book&gt;? &#123;</span><br><span class="line">            return books;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    override fun onBind(intent: Intent?): IBinder? &#123;</span><br><span class="line">        return bookManager</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class BookClientActivity: Activity() &#123;</span><br><span class="line"></span><br><span class="line">    companion object &#123;</span><br><span class="line">        const val TAG &#x3D; &quot;BookClientActivity&quot;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    private var bookManager: BookManager? &#x3D; null</span><br><span class="line">    private var isConnected: Boolean &#x3D; false</span><br><span class="line"></span><br><span class="line">    private val serviceConnection &#x3D; object: ServiceConnection &#123;</span><br><span class="line">        override fun onServiceDisconnected(name: ComponentName?) &#123;</span><br><span class="line">            isConnected &#x3D; false</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        override fun onServiceConnected(name: ComponentName?, service: IBinder?) &#123;</span><br><span class="line">            isConnected &#x3D; true</span><br><span class="line">            bookManager &#x3D; BookManagerStub.asInterface(service)</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    fun toBindService() &#123;</span><br><span class="line">        val intent &#x3D; Intent(this, BookService::class.java)</span><br><span class="line">        intent.setAction(&quot;com.benson.android.aidl.server&quot;)</span><br><span class="line">        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)</span><br><span class="line">    &#125;</span><br><span class="line">｝</span><br></pre></td></tr></table></figure>
<p>其中BookManagerProxy是在Client进程中，而BookManagerStub在Server进程中，BookManagerProxy对BookManager的方法的实现中，使用IBinder的transact方法向Server进程写数据并得到结果，将结果解析成这次调用的返回值。其实这里的这个IBinder对象，就是Server端中new出来的BookManagerStub对象，但如果是同一进程，就是真实的对象，而不是同进程，则是Binder机制让两个进程共享的对象(对象是内存+方法集)。通过打印Log，我发现BookService中的BookManagerStub对象和BookManagerProxy中的IBinder对象，其实不是同一个对象。当然了，两个进程中怎么可能存在同一个对象。我猜这是Binder机制通过一些共享内存，将两个对象之间的差异屏蔽了，让我们调用Client中的对象就像在访问Server中的对象一样。</p>
<p>BookManagerProxy中调用了transct方法就会触发BookManagerStub中的onTransact方法，这里可以通过code来判断是调用的哪个方法，从data中取出Client发来的数据，将调用结果通过replay传回去。</p>
<p>Binder机制虽然是C/S模型，但不存在绝对的C端和S端，按理解应该是主动调的一端就是C端，而被调并且返回结果的一端就是S端。</p>
<ul>
<li>refer: <a class="link"   target="_blank" rel="noopener" href="https://bbs.huaweicloud.com/blogs/detail/237486" >https://bbs.huaweicloud.com/blogs/detail/237486<i class="fas fa-external-link-alt"></i></a></li>
<li><a class="link"   target="_blank" rel="noopener" href="https://blog.csdn.net/tkwxty/article/details/102824924" >https://blog.csdn.net/tkwxty/article/details/102824924<i class="fas fa-external-link-alt"></i></a></li>
<li><a class="link"   target="_blank" rel="noopener" href="https://bbs.huaweicloud.com/blogs/267034" >https://bbs.huaweicloud.com/blogs/267034<i class="fas fa-external-link-alt"></i></a></li>
</ul>
<p>###一．Binder的优点：</p>
<p>##第一. Binder能够很好的实现Client-Server架构</p>
<p>　目前Linux支持的”传统的管道/消息队列/共享内存/信号量/Socket等”IPC通信手段中，（需要经过2次内存拷贝）（发）用户存储空间－＞内核存储空间－＞用户存储空间（收）</p>
<p>　共享内存：进行IPC通信时进行的内存拷贝次数是0．操作复杂</p>
<p>　只有Socket是Client-Server的通信方式。但是，Socket主要用于网络间通信　及　本机　中进程间的低速通信，传输效率太低。</p>
<p>##第二. Binder的传输效率和可操作性很好　</p>
<p>　Binder机制　只需要经过1次内存拷贝即可！从发送方的缓存区拷贝到内核的缓存区．<br>　而接收方的缓存区与内核的缓存区是映射到同一块物理地址的，因此只需要1次拷贝即可</p>
<p>##第三. Binder机制的安全性很高<br> 传统IPC没有任何安全措施，完全依赖上层协议来确保．传统IPC的接收方无法获得对方进程可靠的UID/PID(用户ID/进程ID)，从而无法鉴别对方身份，而Binder机制则为每个进程分配了UID/PID来作为鉴别身份的标示，在实际项目中可能一些敏感的服务，不希望被乱用就可以通过UID/PID鉴权的方式来防护。</p>
<p>Binder通信　使用了内存映射　　（mmap(建立映射) unmap (断开映射)）</p>
<p>理解：<br>Binder实体：内核通过Binder实体可以找到用户空间的Server对象。<br>Binder引用：通过Binder引用可以在内核中找到它对应的Binder实体<br>将Server看作是Binder实体的话，那么Client就好比Binder引用：　每一个Server在内核中就表现为一个Binder实体，而每一个Client则表现为一个Binder引用</p>
<p>Client要和Server通信：<br>它就是通过保存一个Server对象的Binder引用，再通过该Binder引用在内核中找到对应的Binder实体，进而找到Server对象，然后将通信内容发送给Server对象。</p>
<p>Binder引用－＞Binder实体－＞用户空间的Server对象</p>
<p>Binder实体和Binder引用都是内核(即，Binder驱动)中的数据结构</p>
<p>###二． Binder设计</p>
<p>   讲解Binder设计时，分为”内核空间”和”用户空间”这两部分进程讲解。内核空间就是Binder驱动中的Binder设计，而用户空间则是Android的C++层中的Binder设计。</p>
<p>内核空间的Binder设计涉及到3个结构体：binder_proc，binder_node和binder_ref</p>
<ul>
<li>refer：<a class="link"   target="_blank" rel="noopener" href="https://blog.csdn.net/appdsn/article/details/79311455" >https://blog.csdn.net/appdsn/article/details/79311455<i class="fas fa-external-link-alt"></i></a><br>Binder 内存映射：</li>
</ul>
<p>Binder机制的跨进程通信就是使用的　内核空间与用户空间共享数据的原理来实现跨进程的通信</p>
<p>一个进程在使用Binder进程<br>间通信机制之前，首先要调用函数open打开设备文件/dev/binder来获得一个文件描述符， 然后才能通过这个文件描述符来和<br>Binder驱动程序交互， 继而和其他进程执行Binder进程间通信。</p>
<p>进程打开了设备文件/dev/binder之后， 还必须要调用函数mmap把这个设备文件映射到进程的地址空间， 然后才可以使用Binder进程间通信机制。设备文件/dev/binder对应的是一个虚拟的设备， 将它映射到进程的地址空间的目的是为进程分配内核缓冲区， 以便它可以用来传输进程间通信数据。</p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">mVMStart &#x3D; mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);</span><br></pre></td></tr></table></figure>

<p>每个进程都有属于自己的一块binder内存，这块内存的大小就是在进程调用mmap系统调用时分配的，其返回值是就是为用户空间分配的虚拟地址的起始地址，分配的内存大小则由BINDER_VM_SIZE指定，但不能超过4M.PROT_READ属性表示用户空间的进程对内核分配的这个内核缓冲区的内容只读权限.</p>
<p>进程－&gt;执行mmap()函数－＞通过系统调用执行binder驱动的binder_mmap()函数</p>
<p>binder_mmap()：<br>其内部主要为进程的用户空间和内核空间分别分配一段连续的虚拟地址空间（两个地址范围虽不一样，但大小是相同，地址之间相差一个固定值）。接下来会为这两个虚拟地址空间分配内核缓存区，也就是内存映射到同一个物理页，binder的物理页由binder驱动负责分配，然后这些物理页的访问，将分为进程的用户空间和进程内核空间。并且在映射建立初始阶段，只是映射了一个物理页，而不是为整个虚拟地址空间都建立相应的物理影射，目的是充分高效的利用内存，只是到需要用到的时候才建立映射，用完后马上释放映射，这样可以充分高效的利用系统的物理内存页。</p>
<p>进程A向进程B传递数据时，这些数据会被驱动从进程A 拷贝到进程B的内核缓存区中，这个内核缓存区已经映射过地址，进程B就可以直接读取用户空间内存里面的数据．整个过程一次拷贝！！！</p>

        </div>

        
            <div class="post-copyright-info">
                <div class="article-copyright-info-container">
    <ul>
        <li>
            版权声明：本博客所有文章除特别声明外，均采用 <a class="license" target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">BY-NC-SA</a> 许可协议。转载请注明出处！
        </li>
    </ul>
</div>

            </div>
        

        
            <div class="article-nav">
                
                    <div class="article-prev">
                        <a class="prev"
                           rel="prev"
                           href="/2021/04/05/Minidump%E7%9A%84%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/"
                        >
                            <span class="left arrow-icon flex-center">
                              <i class="fas fa-chevron-left"></i>
                            </span>
                            <span class="title flex-center">
                                <span class="post-nav-title-item">Minidump的使用技巧</span>
                                <span class="post-nav-item">上一篇</span>
                            </span>
                        </a>
                    </div>
                
                
                    <div class="article-next">
                        <a class="next"
                           rel="next"
                           href="/2021/01/05/%E8%BF%9B%E7%A8%8B%E9%97%B4%E9%80%9A%E8%AE%AF/"
                        >
                            <span class="title flex-center">
                                <span class="post-nav-title-item">进程间通信方式</span>
                                <span class="post-nav-item">下一篇</span>
                            </span>
                            <span class="right arrow-icon flex-center">
                              <i class="fas fa-chevron-right"></i>
                            </span>
                        </a>
                    </div>
                
            </div>
        

        
    </div>
</div>


                
            </div>

        </div>

        <div class="page-main-content-bottom">
            <footer class="footer">
    <div class="info-container">
        <div class="copyright-info info-item">
            &copy;
            
              <span>2018</span>&nbsp;-&nbsp;
            
            2021&nbsp;<i class="fas fa-heart icon-animate"></i>&nbsp;<a href="/">jianyu</a>
        </div>
        
        
    </div>
</footer>

        </div>
    </div>

    
        <div class="post-tools">
            <div class="post-tools-container">
    <ul class="tools-list">
        <!-- TOC aside toggle -->
        
            <li class="tools-item page-aside-toggle">
                <i class="fas fa-outdent"></i>
            </li>
        

        <!-- go comment -->
        
    </ul>
</div>

        </div>
    

    <div class="right-bottom-side-tools">
        <div class="side-tools-container">
    <ul class="side-tools-list">
        <li class="tools-item tool-font-adjust-plus flex-center">
            <i class="fas fa-search-plus"></i>
        </li>

        <li class="tools-item tool-font-adjust-minus flex-center">
            <i class="fas fa-search-minus"></i>
        </li>

        <li class="tools-item tool-expand-width flex-center">
            <i class="fas fa-arrows-alt-h"></i>
        </li>

        <li class="tools-item tool-dark-light-toggle flex-center">
            <i class="fas fa-moon"></i>
        </li>

        <!-- rss -->
        

        

        <li class="tools-item tool-scroll-to-bottom flex-center">
            <i class="fas fa-arrow-down"></i>
        </li>
    </ul>

    <ul class="exposed-tools-list">
        <li class="tools-item tool-toggle-show flex-center">
            <i class="fas fa-cog fa-spin"></i>
        </li>
        
            <li class="tools-item tool-scroll-to-top flex-center">
                <i class="arrow-up fas fa-arrow-up"></i>
                <span class="percent"></span>
            </li>
        
    </ul>
</div>

    </div>

    
        <aside class="page-aside">
            <div class="post-toc-wrap">
    <div class="post-toc">
        <ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#Binder%E5%88%9D%E4%BA%86%E8%A7%A3"><span class="nav-number">1.</span> <span class="nav-text">Binder初了解:</span></a></li></ol>
    </div>
</div>
        </aside>
    

    <div class="image-viewer-container">
    <img src="">
</div>


    
        <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
          <span class="search-input-field-pre">
            <i class="fas fa-keyboard"></i>
          </span>
            <div class="search-input-container">
                <input autocomplete="off"
                       autocorrect="off"
                       autocapitalize="off"
                       placeholder="搜索..."
                       spellcheck="false"
                       type="search"
                       class="search-input"
                >
            </div>
            <span class="popup-btn-close">
                <i class="fas fa-times"></i>
            </span>
        </div>
        <div id="search-result">
            <div id="no-result">
                <i class="fas fa-spinner fa-pulse fa-5x fa-fw"></i>
            </div>
        </div>
    </div>
</div>

    

</main>




<script src="/js/utils.js"></script>

<script src="/js/main.js"></script>

<script src="/js/header-shrink.js"></script>

<script src="/js/back2top.js"></script>

<script src="/js/dark-light-toggle.js"></script>



    
<script src="/js/local-search.js"></script>




    
<script src="/js/code-copy.js"></script>





<div class="post-scripts">
    
        
<script src="/js/left-side-toggle.js"></script>

<script src="/js/libs/anime.min.js"></script>

<script src="/js/toc.js"></script>

    
</div>



</body>
</html>
